
/*
 * pty.c
 *
 * pty/tty based I/O-Layer
 *
 * 29.06.2010, Klaus Hennemann
 */

#include <pty.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <betty/pty.h>


struct Betty_Pty {
        int     fd;	/* handle of master (pty) */
	pid_t	pid;	/* child pid */
};



int Betty_ForkPty (struct Betty_Pty *pty, const char *filename, 
		char * const argv[], char * const envp[])
{
	struct termios tios;
	pid_t pid;
	int fd;

	if (pty->fd >= 0) {
		fprintf(stderr, "process already running..\n");
		return -1;
	}

	memset(&tios, 0, sizeof(tios));

	/* using sane settings (stty sane) */
	tios.c_iflag = BRKINT | ICRNL | IXON;
	tios.c_oflag = OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0;
	tios.c_cflag = CS8 | HUPCL | CREAD;
	tios.c_lflag = ISIG | ICANON | IEXTEN | 
			ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;

	tios.c_cc[VINTR] 	= 'C'  & 0x1f;
	tios.c_cc[VQUIT] 	= '\\' & 0x1f;
	tios.c_cc[VERASE]	= '?'  & 0x1f;
	tios.c_cc[VKILL]	= 'U'  & 0x1f;
	tios.c_cc[VEOF]		= 'D'  & 0x1f;
	tios.c_cc[VSTART]	= 'Q'  & 0x1f;
	tios.c_cc[VSTOP]	= 'S'  & 0x1f;
	tios.c_cc[VSUSP]	= 'Z'  & 0x1f;
	tios.c_cc[VREPRINT]	= 'R'  & 0x1f;
	tios.c_cc[VWERASE]	= 'W'  & 0x1f;
	tios.c_cc[VLNEXT]	= 'V'  & 0x1f;
	tios.c_cc[VDISCARD]	= 'O'  & 0x1f;
	tios.c_cc[VMIN]		= 1;
	tios.c_cc[VTIME]	= 0;

	if ( (pid = forkpty(&fd, NULL, &tios, NULL)) < 0 ) {
		perror("forkpty()");
		return -1;
	}

	if (pid == 0) {
		execve(filename, argv, envp);
		return -1;
	}

	pty->fd = fd;
	pty->pid = pid;

	return 0;
}


int Betty_KillPtyChild (struct Betty_Pty *pty)
{
	int status;

	if (pty->fd < 0)
		return 0;

	kill(pty->pid, SIGKILL); 

	if (waitpid(pty->pid, &status, 0) < 0)
		return -1;

	close(pty->fd);
	pty->fd = -1;

	return status;
}


int Betty_ReadPtyData (struct Betty_Pty *pty, 
			void *buf, unsigned int count)
{
	return read(pty->fd, buf, count);
}


int Betty_WritePtyData (struct Betty_Pty *pty, 
			const void *buf, unsigned int count)
{
	return write(pty->fd, buf, count);
}


struct Betty_Pty * Betty_CreatePty ()
{
	struct Betty_Pty *pty;

	if ( !(pty = (struct Betty_Pty *)malloc(sizeof(*pty))) )
		return NULL;

	pty->fd = -1;
	pty->pid = 0;

	return pty;
}


void Betty_DestroyPty (struct Betty_Pty *pty)
{
	Betty_KillPtyChild(pty);
	free(pty);
}


